import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { ALLOW_ANON_KEY, PUBLIC_KEY } from '../auth/auth.constant';
import { FastifyRequest } from 'fastify';
import { ErrorEnum } from '~/constants/error-code.constant';
import { BusinessException } from '~/common/exceptions/biz.exception';
import { TenantService } from './tenant.service';
import { isNil } from 'lodash';

@Injectable()
export class TenantGuard implements CanActivate {
  constructor(private reflector: Reflector, private readonly tenantService: TenantService) {}

  async canActivate(context: ExecutionContext): Promise<any> {
    const isPublic = this.reflector.getAllAndOverride<boolean>(PUBLIC_KEY, [
      context.getHandler(),
      context.getClass(),
    ]);

    const request = context.switchToHttp().getRequest<FastifyRequest>();

    const allowAnon = this.reflector.get<boolean>(ALLOW_ANON_KEY, context.getHandler());
    if (allowAnon) return true;

    if (isPublic) {
      // 读取请求头是否有 TenantCode
      const tenantCode = request.headers['tenant-code'] as string;

      if (tenantCode) {
        const tenant = await this.tenantService.getTenantByCode(tenantCode);

        if (!isNil(tenant)) {
          request['tenant'] = tenant;
        }
      }

      return true;
    }

    const { user } = request;

    if (!user) throw new BusinessException(ErrorEnum.INVALID_LOGIN);

    if (user.isSuperAdmin) {
      return true;
    }

    if (user.isAdmin) {
      if (!user?.tenant?.id) {
        throw new BusinessException(ErrorEnum.NO_PERMISSION);
      }

      request['tenant'] = user.tenant;
      return true;
    }

    // 读取请求头是否有 TenantCode
    const tenantCode = request.headers['tenant-code'] as string;

    if (!tenantCode) {
      throw new BusinessException(ErrorEnum.NO_PERMISSION);
    }

    const tenant = await this.tenantService.getTenantByCode(tenantCode);

    if (!tenant) {
      throw new BusinessException(ErrorEnum.NO_PERMISSION);
    }

    request['tenant'] = tenant;

    return true;
  }
}
